#include	"alp_global_vars.h"
#include	"config_parser.h"

void compute_performance(int sd, short event, void* arg) {
	if (rtag) {
		gettimeofday(&performance_end_time, NULL);
		gettimeofday(&endTime, NULL);

		alp_interval_performance->interval_time = measure_time_difference(performance_end_time, performance_start_time);
		alp_interval_performance->total_time = measure_time_difference(endTime, startTime);

		alp_interval_performance->interval_sending_throughput = calculate_throughput((alp_interval_performance->interval_bytes_sent), alp_interval_performance->interval_time);
		alp_interval_performance->interval_recv_throughput = calculate_throughput(alp_interval_performance->interval_bytes_received, alp_interval_performance->interval_time);

		alp_interval_performance->interval_delivery_throughput = calculate_throughput(alp_interval_performance->interval_num_packets_delivered * (sizeof(proposer_msg) - PROPOSER_MSG_HEADER), alp_interval_performance->interval_time);
		alp_interval_performance->interval_propos_thr = calculate_throughput(alp_interval_performance->interval_bytes_proposed, alp_interval_performance->interval_time);

		if (alp_interval_performance->num_packets_delivered_latency == 0)
			alp_interval_performance->num_packets_delivered_latency = 0;
		else
			alp_interval_performance->interval_latency = alp_interval_performance->interval_latency / (double) (alp_interval_performance->num_packets_delivered_latency * MAX_MSG_COUNT);
		alp_interval_performance->total_num_packets_delivered += alp_interval_performance->interval_num_packets_delivered;

		alp_calculate_performance_over_all_interval_uptonow();
		alp_store_performance_in_array(&alp_performances[0], &alp_next_performance_index, alp_interval_performance);
		alp_print_performance(alp_interval_performance);
		alp_nulify_performance(alp_interval_performance);

		if (alp_next_performance_index >= STOPPING_CONDITION) {
			alp_store_performance_in_file(alp_performance_file, alp_latency_file, alp_sd_avg_file, alp_next_performance_index);
			free(data);
			int i;
			for (i = 0; i < get_config_info()->total_num_acceptors; i++)
				free(p_msgs[i]);
			//			sleep(4);
			exit(1);
		}
	}
	gettimeofday(&performance_start_time, NULL);
	int status = evtimer_add(print_stat_timeout_eve, &tv1);
	assert(status >= 0);
}
double measure_time_difference(struct timeval now, struct timeval past) {
	struct timeval elapsed_interval;

	elapsed_interval.tv_sec = now.tv_sec - past.tv_sec;
	elapsed_interval.tv_usec = (now.tv_usec > past.tv_usec ? now.tv_usec - past.tv_usec : past.tv_usec - now.tv_usec);
	double msec_interval = 1000 * elapsed_interval.tv_sec + elapsed_interval.tv_usec / (double) 1000;

	return msec_interval;
}
double calculate_throughput(int bytes, double elapsed_time) {
	double thr = (bytes / (double) (1024 * 1024));
	thr = thr / (double) elapsed_time;
	thr = thr * 1000 * 8;
	return thr;
}
void alp_calculate_performance_over_all_interval_uptonow() {
	int i;
	double avg_t = 0, avg_s_t = 0, avg_l = 0;

	for (i = 0; i < alp_next_performance_index; i++) {
		avg_s_t += alp_performances[i].interval_sending_throughput;
		avg_t += alp_performances[i].interval_delivery_throughput;
		avg_l += alp_performances[i].interval_latency;
	}
	alp_interval_performance->total_sending_throughput = (avg_s_t + alp_interval_performance->interval_delivery_throughput) / (double) (alp_next_performance_index + 1);
	alp_interval_performance->total_delivery_throughput = (avg_t + alp_interval_performance->interval_delivery_throughput) / (double) (alp_next_performance_index + 1);

	alp_interval_performance->total_latency = (avg_l + alp_interval_performance->interval_latency) / (double) (alp_next_performance_index + 1);
}
void alp_store_performance_in_array(alp_performance * alp_performances_array, int * performance_index, alp_performance *i_p) {
	memcpy((alp_performances_array + *performance_index), i_p, sizeof(alp_performance));
	*(performance_index) = *(performance_index) + 1;
}

void alp_print_performance(alp_performance *p) {
	printf("------------------------------------------------------------------------------------------------------------------\n");
	printf("TOT_TIME:%f	   INTER_TIME: %f  		TICKS_PER_SECOND: %f\n\n", p->total_time, p->interval_time, p->interval_num_entrance);
	printf("INTERVAL prop: 	   THROUGHPUT: %f	BYTES:%f\n", p->interval_propos_thr, p->interval_bytes_proposed);
	printf("INTERVAL SENT: 	   THROUGHPUT: %f	BYTES:%f\n", p->interval_sending_throughput, p->interval_bytes_sent);
	printf("INTERVAL RECV: 	   THROUGHPUT: %f	BYTES:%f\n", p->interval_recv_throughput, p->interval_bytes_received);
	printf("INTERVAL DELI: 	   THROUGHPUT: %f	NUM_PACKETS: %d, Own_NUM_PACKETS: %d\n\n", p->interval_delivery_throughput, p->interval_num_packets_delivered, alp_interval_performance->num_packets_delivered_latency);
	printf("LATENCY: %f			PROP_PER_WAKE: %d\n", p->interval_latency, PROP_PER_WAKE);
	printf("PROPOSER: %d	self_hd: %d total_hd: %d\n", p->next_location_index_in_proposer, p->highest_self_deliverable, p->highest_deliverable_inst_id);
	printf("Interrupts: %d	(%d) (%d)\n", p->num_interrupts_new_msgs, p->num_interrupts_phase2b, p->num_interrupts_phase2b_individual);
	//	printf("TOTAL DELIVERY:    NUM_PACKETS: %d, THROUGHPUT: %f\n", p->total_num_packets_delivered, p->total_delivery_throughput);
	//	printf("TOTAL SENT:        NUM_PACKETS: %d, THROUGHPUT: %f\n\n", p->total_num_packets_sent, p->total_sending_throughput);
	//	printf("INTERVAL REDUNDANT PACKETS:     %d  LOST_PACKETS: %d\n", p->interval_num_redundant_packets_received, p->interval_num_packets_lost);
	//	printf("TOTAL    REDUNDANT PACKETS:     %d\n\n", p->total_num_redundant_packets_received);

}

void alp_store_performance_in_file(const char * performance_file, const char * latency_file, const char * sd_avg_file, int num_entries) {

	FILE *fp;
	int i;

	fp = fopen(performance_file, "w");
	assert(fp != NULL);

	for (i = DISCARD / 1000; i < num_entries - DISCARD / 1000; i++) {

		fprintf(fp, "%d %d %d %d %f %f %f %f %f %f %f %f %d %f %f %d\n", i + 1, //1
				SUBMIT_PER_WAKE, //2
				PROP_PER_WAKE, //3
				(int) tv2.tv_usec, //4
				alp_performances[i].interval_time, //5
				alp_performances[i].interval_delivery_throughput, //6
				alp_performances[i].interval_sending_throughput, //7
				alp_performances[i].interval_recv_throughput, //8
				alp_performances[i].interval_propos_thr, //9
				alp_performances[i].interval_bytes_sent, //10
				alp_performances[i].interval_bytes_received, //11
				alp_performances[i].interval_bytes_proposed, //12
				alp_performances[i].interval_num_packets_delivered, //13
				alp_performances[i].interval_num_entrance, //14
				alp_performances[i].interval_latency, //15
				alp_performances[i].num_interrupts); //16
	}

	fclose(fp);

	fp = fopen(latency_file, "w");
	assert(fp != NULL);

	for (i = 0; i < alp_interval_performance->latency_counter; i++)
		fprintf(fp, "%f\n", latency[i]);

	fclose(fp);

	//calculate avg and sdv
	alp_interval_performance->latency_avg = alp_interval_performance->latency_sum / alp_interval_performance->latency_counter;
	alp_interval_performance->latency_sd = alp_interval_performance->latency_sq_sum - 2 * alp_interval_performance->latency_avg * alp_interval_performance->latency_sum + alp_interval_performance->latency_counter * pow(alp_interval_performance->latency_avg, 2);
	alp_interval_performance->latency_sd = alp_interval_performance->latency_sd / alp_interval_performance->latency_counter;
	alp_interval_performance->latency_sd = sqrt(alp_interval_performance->latency_sd);

	fp = fopen(sd_avg_file, "w");
	assert(fp != NULL);
	fprintf(fp, "%f %f %d\n", alp_interval_performance->latency_avg, alp_interval_performance->latency_sd, alp_interval_performance->latency_counter);
	fclose(fp);

}
void alp_nulify_performance(alp_performance *p) {
	p->interval_time = 0;
	p->interval_num_packets_delivered = 0;
	p->interval_delivery_throughput = 0;
	p->interval_num_packets_sent = 0;
	p->interval_sending_throughput = 0;
	p->interval_num_entrance = 0;
	p->interval_latency = 0;
	p->interval_num_packets_received = 0;
	p->interval_num_packets_proposer_to_acceptor_0 = 0;
	p->interval_bytes_sent = 0;
	p->interval_bytes_received = 0;
	p->interval_num_packets_proposed = 0;
	p->interval_propos_thr = 0;
	p->num_packets_delivered_latency = 0;
	p->interval_bytes_proposed = 0;

	p->num_interrupts_new_msgs = 0;
	p->num_interrupts_phase2b = 0;
	p->num_interrupts_phase2b_individual = 0;
	p->num_interrupts = 0;


	if (alp_next_performance_index == 0) {
		p->total_time = 0;
		p->total_num_packets_delivered = 0;
		p->total_delivery_throughput = 0;
		p->total_num_packets_sent = 0;
		p->total_sending_throughput = 0;
		p->total_num_entrance = 0;
		p->total_latency = 0;
		p->total_num_packets_received = 0;
		p->total_num_packets_proposed = 0;
		p->next_location_index_in_proposer = 0;
		p->highest_self_deliverable = -1;
		p->highest_deliverable_inst_id = -1;
		p->next_instance_id = 0;
		p->latency_counter = 0;
		p->latency_avg = 0;
		p->latency_sd = 0;
		p->latency_sq_sum = 0;
		p->latency_sum = 0;

	}
}

